package com.yanqu.road.server.manger.player;

import com.alibaba.fastjson.JSON;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.Message;
import com.yanqu.road.connect.ServerLinkControl;
import com.yanqu.road.entity.enums.eChannelType;
import com.yanqu.road.entity.enums.eMailType;
import com.yanqu.road.entity.log.LogLogin;
import com.yanqu.road.entity.player.PlayerState;
import com.yanqu.road.entity.player.UserInfo;
import com.yanqu.road.entity.servercenter.ServerInfo;
import com.yanqu.road.logic.bussiness.player.UserBussiness;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.logic.helper.UnionActivityGroupHelper;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.activity.ActivityProto;
import com.yanqu.road.pb.common.CommonProto;
import com.yanqu.road.pb.cross3.server.CrossChannelRouteProto;
import com.yanqu.road.pb.union.UnionActivityGroupProto;
import com.yanqu.road.queue.AutoDriveQueue;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.agent.inter.AgentPlayer;
import com.yanqu.road.server.cmd.activity.curio.CurioWatchItemServiceCmd;
import com.yanqu.road.server.cmd.activity.curio.CurioWatchUserServiceCmd;
import com.yanqu.road.server.cmd.activity.qixi.QixiRelationTopGetListServiceCmd;
import com.yanqu.road.server.cmd.activity.summergames.SummerGamesSearchUserServiceCommand;
import com.yanqu.road.server.game.service.GameService;
import com.yanqu.road.server.game.service.GoddessWatchUserServiceCmd;
import com.yanqu.road.server.gameplayer.module.activity.hsisland.HsIslandActivityModule;
import com.yanqu.road.server.gameplayer.module.activity.signindraw.SignInDrawModule;
import com.yanqu.road.server.gameplayer.module.activity.tomb.TombMgr;
import com.yanqu.road.server.game.service.WineryVisitICrossServiceCmd;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.cricket.CricketMgr;
import com.yanqu.road.server.gameplayer.module.cricket.service.CricketDefendHandleBattleLogServiceCmd;
import com.yanqu.road.server.gameplayer.module.cricket.service.CricketDefendHandleSuccessServiceCmd;
import com.yanqu.road.server.gameplayer.module.mall.MallModule;
import com.yanqu.road.server.gameplayer.module.manor.ManorModule;
import com.yanqu.road.server.gameplayer.module.patrons.PatronsModule;
import com.yanqu.road.server.gameplayer.module.player.DailyDataModule;
import com.yanqu.road.server.gameplayer.module.player.OreWarModule;
import com.yanqu.road.server.gameplayer.module.player.PlayerModule;
import com.yanqu.road.server.gameplayer.module.userinfo.service.UserInfoDetailServiceCmd;
import com.yanqu.road.server.logic.mail.MailManager;
import com.yanqu.road.server.manger.*;
import com.yanqu.road.server.manger.activity.dunhuangshiku.DunHuangShiKuMgr;
import com.yanqu.road.server.manger.activity.evefeast.EveFeastMgr;
import com.yanqu.road.server.manger.activity.feast.ShanHeFeastMgr;
import com.yanqu.road.server.manger.activity.ghost.GhostSceneLogic;
import com.yanqu.road.server.manger.activity.guagifts.GuaGiftsMgr;
import com.yanqu.road.server.manger.activity.jiuzhou.JiuZhouMgr;
import com.yanqu.road.server.manger.activity.peakseacraft.PeakSeacraftMgr;
import com.yanqu.road.server.manger.activity.qixi.QixiMgr;
import com.yanqu.road.server.manger.activity.recallplayer.RecallPlayerMgr;
import com.yanqu.road.server.manger.activity.seacraft.SeacraftSouthSeaMgr;
import com.yanqu.road.server.manger.activity.shuihuyezicard.ShuiHuYeZiCardMgr;
import com.yanqu.road.server.manger.activity.shushan.ShuShanMgr;
import com.yanqu.road.server.manger.activity.silkroad.SilkRoadMgr;
import com.yanqu.road.server.manger.activity.snowgarden.ActivityMallDayBuyMgr;
import com.yanqu.road.server.manger.activity.snowgarden.SnowGardenMgr;
import com.yanqu.road.server.manger.activity.xiuxian.XiuXianMgr;
import com.yanqu.road.server.manger.activity.zhenhuan.ZhenHuanMgr;
import com.yanqu.road.server.manger.banquet.BanquetMgr;
import com.yanqu.road.server.manger.banquet.CrossBanquetMgr;
import com.yanqu.road.server.manger.hunt.HuntMgr;
import com.yanqu.road.server.manger.kuang.KuangMgr;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.manger.monopolymarket.MonopolyMarketMgr;
import com.yanqu.road.server.manger.sacrifice.SacrificeMgr;
import com.yanqu.road.server.manger.systemgroup.newmonopolymarket.NewMonopolyMarketMgr;
import com.yanqu.road.server.manger.unioncampwar.UnionCampWarPatronsMgr;
import com.yanqu.road.server.manger.union.GameUnionActivityGroupMgr;
import com.yanqu.road.server.protocol.*;
import com.yanqu.road.server.push.PushMgr;
import com.yanqu.road.sync.message.Caller;
import com.yanqu.road.sync.message.ResultProcessor;
import com.yanqu.road.sync.message.SyncResponse;
import com.yanqu.road.utils.MD5Security;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.http.HttpHelper;
import com.yanqu.road.utils.string.StringUtils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

public class GamePlayerMgr extends TempMgr {

    private static Logger logger = LogManager.getLogger(GamePlayerMgr.class.getName());

    private static Map<Long, GamePlayer> playerMap = new ConcurrentHashMap<>();
    /**
     * 玩家PlayerMap 锁
     */
    private static final Map<Long, Object> PLAYER_LOCK_MAP = new ConcurrentHashMap<>();
    /**
     * 玩家保存使用的锁
     */
    private static final Map<Long, Object> PLAYER_SAVE_LOCK_MAP = new ConcurrentHashMap<>();

    private static AutoDriveQueue<Runnable> removePlayerQueue;

    private static final Caller syncMessageCaller = new Caller();

    private static final GameService syncMessageService = new GameService();

    public static Object getPlayerLock(long userId) {
        if (!PLAYER_LOCK_MAP.containsKey(userId)) {
            synchronized (PLAYER_LOCK_MAP) {
                if (!PLAYER_LOCK_MAP.containsKey(userId)) {
                    PLAYER_LOCK_MAP.put(userId, new Object());
                }
            }
        }
        return PLAYER_LOCK_MAP.get(userId);
    }

    public static Object getPlayerSaveLock(long userId) {
        if (!PLAYER_SAVE_LOCK_MAP.containsKey(userId)) {
            synchronized (PLAYER_SAVE_LOCK_MAP) {
                if (!PLAYER_SAVE_LOCK_MAP.containsKey(userId)) {
                    PLAYER_SAVE_LOCK_MAP.put(userId, new Object());
                }
            }
        }
        return PLAYER_SAVE_LOCK_MAP.get(userId);
    }

    private static volatile boolean dbConnect = true;

    @Override
    public boolean stop() {
        return false;
    }

    @Override
    public boolean save() {
        return false;
    }

    @Override
    public boolean reload() {
        return true;
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean reloadData() throws Exception {
        return true;
    }

    @Override
    public boolean init() {
        playerMap = new ConcurrentHashMap<>();
        addChannelConfigReloadListener();
        addServerConfigReloadListener();
        addGameConfigReloadListener();
        addServerGroupReloadListener();
        addMarriageGroupReloadListener();
        addServerGroup2ReloadListener();
        addMallRebateReloadListener();
        return true;
    }

    private void addChannelConfigReloadListener() {
        ChannelConfigMgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(PlayerModule.class).syncChannelConfig();
                player.getModule(PlayerModule.class).syncExpressionShielding();
                player.getModule(ManorModule.class).linleeSkinCheck(true);
            }
        });
    }

    private void addServerConfigReloadListener() {
        ServerConfigMgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(PlayerModule.class).syncServerConfig();
            }
        });
    }

    private void addGameConfigReloadListener() {
        ConfigMgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(PlayerModule.class).syncGameConfig();
            }
        });
    }

    private void addServerGroupReloadListener() {
        ServerGroupMgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(PlayerModule.class).syncServerGroup();
            }
        });
    }

    private void addServerGroup2ReloadListener() {
        ServerGroup2Mgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(PlayerModule.class).syncAreaMammonGroup();
            }
        });
    }

    public static List<Long> getAllOnlinePlayerId() {
        List<Long> list = new ArrayList<>();
        if (playerMap != null) {
            for (Entry<Long, GamePlayer> entry : playerMap.entrySet()) {
                if (entry.getValue().getState() == PlayerState.ONLINE) {
                    list.add(entry.getKey());
                }
            }
        }
        return list;
    }

    public static List<GamePlayer> getAllOnlinePlayer() {
        List<GamePlayer> list = new ArrayList<>();
        if (playerMap != null) {
            for (Entry<Long, GamePlayer> entry : playerMap.entrySet()) {
                if (entry.getValue().getState() == PlayerState.ONLINE) {
                    list.add(entry.getValue());
                }
            }
        }
        return list;
    }

    public static Map<Long, GamePlayer> getCopyPlayerMap() {
        return new HashMap<>(playerMap);
    }

    public static Map<Long, GamePlayer> getPlayerMap() {
        return playerMap;
    }

    public static List<GamePlayer> getCopyPlayerList() {
        return new ArrayList<>(playerMap.values());
    }

    public static GamePlayer getOnlinePlayer(long userId) {
        return playerMap.get(userId);
    }

    public static GamePlayer getPlayer(long userId) {
        synchronized (getPlayerLock(userId)) {
            //先获得玩家锁
            GamePlayer player = playerMap.get(userId);
            if (player != null) {
                return player;
            }

            player = loadPlayer(userId);
            if (player == null) {
                return null;
            }

            playerMap.put(userId, player);
            logger.info("user {} put in memory.", userId);
            return playerMap.get(userId);
        }
    }

    public static GamePlayer getPlayer(long userId, String openId) {
        synchronized (getPlayerLock(userId)) {
            GamePlayer player = playerMap.get(userId);
            if (player != null) {
                return player;
            }

            player = loadPlayer(userId, openId);
            if (player == null) {
                return null;
            }

            playerMap.put(userId, player);
            logger.info("user {} put in memory.", userId);
            return playerMap.get(userId);
        }
    }


    public static boolean timeSave(int saveIndex) {
        int saveUserDataThreadCount = Runtime.getRuntime().availableProcessors();
       /* List<GamePlayer> players = new ArrayList<>(playerMap.values());
        players.forEach(action -> {
            if ((action.getUserId() % saveUserDataThreadCount) == saveIndex) {
                action.save();
            }
        });*/
        List<Long> userIds = new ArrayList<>(playerMap.keySet());
        for (Long userId : userIds) {
            if (userId % saveUserDataThreadCount == saveIndex) {
                synchronized (getPlayerSaveLock(userId)) {
                    GamePlayer player = playerMap.get(userId);
                    if (player != null) {
                        player.saveData();
                    }
                }
            }
        }
        return true;
    }

    private static GamePlayer loadPlayer(long userId, String openId) {
        UserInfo userInfo = UserBussiness.getUserInfoByUserId(userId, openId);
        if (null == userInfo) {
            logger.error("not find userId = " + userId);
            return null;
        }
        if (StringUtils.isNullOrEmpty(userInfo.getNickName()) || 0 == userInfo.getSex()) {
            logger.error("not create role, userId = " + userId);
            return null;
        }
        GamePlayer player = new GamePlayer(userInfo);
        try {
            if(!player.loadData()){
                return null;
            }
            player.afterLoadData();
        }catch (Exception e){
            logger.error("load player afterLoadDB error," + e.getMessage(), e);
            return null;
        }
        return player;
    }


    private static GamePlayer loadPlayer(long userId) {
        //添加保存锁，保证保存和加载不会同时发生
        synchronized (getPlayerSaveLock(userId)) {
            UserInfo userInfo = UserBussiness.getUserInfoByUserId(userId);
            if (null == userInfo) {
                logger.error("not find userId = " + userId);
                return null;
            }
            if (StringUtils.isNullOrEmpty(userInfo.getNickName()) || 0 == userInfo.getSex()) {
                logger.error("not create role, userId = " + userId);
                return null;
            }
            GamePlayer player = new GamePlayer(userInfo);
            try {
                if(!player.loadData()){
                    return null;
                }
                player.afterLoadData();
            }catch (Exception e){
                logger.error("load player afterLoadDB error," + e.getMessage(), e);
                return null;
            }
            return player;
        }
    }

    public static void onLogout(GamePlayer player, boolean isKick) {
        GamePlayer oldPlayer;
        if (player != null) {
            oldPlayer = getOnlinePlayer(player.getUserId());//获取当前角色的GamePlayer
            if (oldPlayer == null) {
                logger.warn("Disconnect player, userId = " + player.getUserId());
                return;
            }
            // 用户下线操作
            playerOffLine(oldPlayer);
            // 移除系统标识逻辑
            removeSystem(oldPlayer);
            // 如果是剔除 才移出内存
            if (isKick) {
                removeFromMap(oldPlayer.getUserId());
            }

        }
    }

    private static void playerOffLine(GamePlayer player) {
        player.setState(PlayerState.OFFLINE);
        player.unLoadData();
        //上报游戏时长
        PushMgr.buildGameTime(player, (int) (System.currentTimeMillis() / 1000));

        // 下线记录
        AutoLogMgr.add(new LogLogin(player.getUserId(),
                1,
                player.getIp(),
                player.getDeviceId(),player.getUserInfo().getEarnSpeed().toString(),
                player.getUserInfo().getSilver().toString(),
                player.getModule(PatronsModule.class).getTotalPatronsAbility().toString()
        ));
        pushOfflineReport(player);

        //在线时长：客户端实现
        //登出
        DataAnalyticsMgr.userLogout(player);
    }

    private static void pushOfflineReport(GamePlayer player) {
        try {
            int channelId = ServerListMgr.getChannelId(GameServer.getInstance().getServerId());
            if (eChannelType.isMainLandChannel(channelId) && !eChannelType.isXiaoChengxuChannel(channelId)) {
                GameServer.getSqlCommandExecutor().execute(() -> {
                    try {
                        String appId = player.getUserInfo().getAppId();
                        String game_id = player.getUserInfo().getPlatformUserId();
                        int type = 1002;
                        String uid = "" + player.getUserId();
                        int sid = (int) GameServer.getInstance().getServerId();
                        String secret = AppKeyMgr.getAndroidAppSecret();
                        int time = (int) (System.currentTimeMillis() / 1000);
                        StringBuilder sb = new StringBuilder();
                        sb.append(secret).append(appId).append(game_id).append(uid).append(sid).append(type).append(time);
                        String sign = MD5Security.md5(sb.toString()).toLowerCase();

                        SortedMap<String, Object> xxxMap = new TreeMap<>();
                        xxxMap.put("appid", appId);
                        xxxMap.put("game_id", game_id);
                        xxxMap.put("type", type);
                        xxxMap.put("uid", uid);
                        xxxMap.put("time", time);
                        xxxMap.put("sid", sid);
                        xxxMap.put("sign", sign);
                        String s = JSON.toJSONString(xxxMap);

                        String url = AppKeyMgr.getPlatformBaseUrl() + "/player/lineReport/" + AppKeyMgr.getAndroidAppId() + "/" + AppKeyMgr.getPlatformChannelId();
                        String result = HttpHelper.doPostJson(url, s, "UTF-8");
                        GamePlayerMgr.getLogger().info("{} {} upload logout resp: {}", url, player.getUserId(),result);
                    } catch (Exception e) {
                        GamePlayerMgr.getLogger().error("offline report error.",e);
                    }
                });
            }
        } catch (Exception e) {
            GamePlayerMgr.getLogger().error("offline report error.",e);
        }
    }

    public static void pushOnlineReport(GamePlayer player, ServerInfo serverInfo) {
        try {
            if (eChannelType.isMainLandChannel(serverInfo.getChannelId()) && !eChannelType.isXiaoChengxuChannel(serverInfo.getChannelId())) {
                GameServer.getSqlCommandExecutor().execute(() -> {
                    try {
                        String appId = player.getUserInfo().getAppId();
                        String game_id = player.getUserInfo().getPlatformUserId();
                        int type = 1001;
                        String uid = "" + player.getUserId();
                        int sid = (int) serverInfo.getServerId();
                        String secret = AppKeyMgr.getAndroidAppSecret();
                        int time = (int) (System.currentTimeMillis() / 1000);
                        StringBuilder sb = new StringBuilder();
                        sb.append(secret).append(appId).append(game_id).append(uid).append(sid).append(type).append(time);
                        String sign = MD5Security.md5(sb.toString()).toLowerCase();

                        SortedMap<String, Object> xxxMap = new TreeMap<>();
                        xxxMap.put("appid", appId);
                        xxxMap.put("game_id", game_id);
                        xxxMap.put("type", type);
                        xxxMap.put("uid", uid);
                        xxxMap.put("time", time);
                        xxxMap.put("sid", sid);
                        xxxMap.put("sign", sign);
                        String s = JSON.toJSONString(xxxMap);

                        String url = AppKeyMgr.getPlatformBaseUrl() + "/player/lineReport/" + AppKeyMgr.getAndroidAppId() + "/" + AppKeyMgr.getPlatformChannelId();
                        String result = HttpHelper.doPostJson(url, s, "UTF-8");
                        GamePlayerMgr.getLogger().info("{} {} upload login resp: {}", url, player.getUserId(),result);
                    } catch (Exception e) {
                        GamePlayerMgr.getLogger().error("online report error.",e);
                    }
                });
            }

        } catch (Exception e) {
            GamePlayerMgr.getLogger().error("online report error.",e);
        }
    }

    private static void removeFromMap(long userId) {
        //先持有玩家锁，保证获得玩家和移出玩家不会同时发生
        synchronized (getPlayerLock(userId)) {
            //从狩猎场景集合中移除
            HuntMgr.removeUserInHuntScene(userId);
            SacrificeMgr.removeUserInSacrificeScene(userId);
            //从宴会场景中移除
            BanquetMgr.removeAllBanquetScene(userId);
            // 从鬼市场景中移除
            GhostSceneLogic.quitRoom(userId);
            GamePlayer player = getOnlinePlayer(userId);
            if (player != null && player.getState() == PlayerState.OFFLINE) {
                //移出内存的时候，先获得玩家的save锁，保证和定时任务的save互斥
                synchronized (getPlayerSaveLock(userId)) {
                    //保存活动逗留时间日志
                    player.getModule(PlayerModule.class).saveActivityRemainTime();
                    // 保存系统逗留时间日志
                    player.getModule(PlayerModule.class).saveSystemRemainTime();
                    player.getModule(OreWarModule.class).patronsAbilityChangeTimeSync();
                    //记录玩家在线时长
                    player.logOnlineTime();
                    if (!dbConnect) {
                        return;
                    }
                    player.saveData();
                    if (!dbConnect) {
                        return;
                    }
                    //清理一下不必要的数据
                    BlacklistMgr.offlineClearCrossBlacklistUserInfo(userId);
                    playerMap.remove(userId);
                    logger.info("user {} remove memory.", userId);
                    player.loggerErrorSql();
                }
            }
        }
    }

    public static void sendPacket(GamePlayer player, YanQuMessage packet) {
        if (player != null && packet != null) {
            sendPacket(player.getUserId(), packet);
        }
    }

    public static void sendPacket(long userId, YanQuMessage packet) {
        ServerLinkControl.send(userId, packet);
    }

    public static void sendChannelPacket(int code, AbstractMessage.Builder<?> messageBuilder, long userId) {
        CrossChannelRouteProto.RouteChannelMsg.Builder builder = CrossChannelRouteProto.RouteChannelMsg.newBuilder();
        builder.setChannelCode(code);
        builder.setMsg(messageBuilder.build().toByteString());
        YanQuMessage message = YanQuMessageUtils.buildMessage(Cross3Protocol.C3_ROUTE_CHANNEL_MSG, builder);
        ServerLinkControl.send(userId, message);
    }

    public static void sendChannelPlayerPacket(int code, AbstractMessage.Builder<?> messageBuilder, long userId) {
        CrossChannelRouteProto.RouteChannelMsg.Builder builder = CrossChannelRouteProto.RouteChannelMsg.newBuilder();
        builder.setChannelCode(code);
        builder.setMsg(messageBuilder.build().toByteString());
        YanQuMessage message = YanQuMessageUtils.buildMessage(Cross3Protocol.C3_ROUTE_CHANNEL_MSG, userId,builder);
        ServerLinkControl.send(userId, message);
    }

    /**
     * 保证跨域解包固定获得渠道和区服数据,跨域服用CrossChannelCommand接收
     * @param code
     * @param messageBuilder
     * @param userId
     */
    public static void sendChannelPlayerPacketEx(int code, AbstractMessage.Builder<?> messageBuilder, long userId) {
        long serverId = GameServer.getInstance().getServerId();
        int channelId = ServerListMgr.getChannelId(serverId);

        CrossChannelRouteProto.CrossChannelCommandMsg.Builder channelMsg = CrossChannelRouteProto.CrossChannelCommandMsg.newBuilder();
        channelMsg.setChannelId(channelId);
        channelMsg.setServerId(serverId);
        channelMsg.setMsg(messageBuilder.build().toByteString());

        CrossChannelRouteProto.RouteChannelMsg.Builder builder = CrossChannelRouteProto.RouteChannelMsg.newBuilder();
        builder.setChannelCode(code);
        builder.setMsg(channelMsg.build().toByteString());
        YanQuMessage message = YanQuMessageUtils.buildMessage(Cross3Protocol.C3_ROUTE_CHANNEL_MSG, userId,builder);
        ServerLinkControl.send(userId, message);
    }

    public static void sendUnionActivityGroupPacket(int code, AbstractMessage.Builder<?> messageBuilder, long userId, int activityId, int groupId) {

        // 没组不能发
        if (groupId == UnionActivityGroupHelper.NO_GROUP_ID) {
            return;
        }

        boolean isActivityCrossCode = code > 40000 && code < 50000;
        if (!isActivityCrossCode) {
            throw new RuntimeException("============================= 这个不是活动跨服协议ID，用这个发要死循环了 =============================");
        }

        UnionActivityGroupProto.BaseUnionActivityGroupMsg.Builder newBuilder = UnionActivityGroupProto.BaseUnionActivityGroupMsg.newBuilder()
                .setActivityId(activityId)
                .setGroupId(groupId)
                .setCode(code)
                .setMsg(messageBuilder.build().toByteString());
        YanQuMessage message = YanQuMessageUtils.buildMessage(AgentProtocol.A_UNION_ACTIVITY_GROUP_TRANSFER, userId, newBuilder);
        ServerLinkControl.send(userId, message);
    }

    public static void sendUnionActivityGroupPacket(int code, Message message, long userId, int activityId, int groupId) {

        // 没组不能发
        if (groupId == UnionActivityGroupHelper.NO_GROUP_ID) {
            return;
        }

        boolean isActivityCrossCode = code > 40000 && code < 50000;
        isActivityCrossCode = isActivityCrossCode || code == 99998;
        if (!isActivityCrossCode) {
            throw new RuntimeException("============================= 这个不是活动跨服协议ID，用这个发要死循环了 =============================");
        }

        UnionActivityGroupProto.BaseUnionActivityGroupMsg.Builder newBuilder = UnionActivityGroupProto.BaseUnionActivityGroupMsg.newBuilder()
                .setActivityId(activityId)
                .setGroupId(groupId)
                .setCode(code)
                .setMsg(message.toByteString());
        YanQuMessage yanQuMessage = YanQuMessageUtils.buildMessage(AgentProtocol.A_UNION_ACTIVITY_GROUP_TRANSFER, userId, newBuilder);
        ServerLinkControl.send(userId, yanQuMessage);
    }

    /**
     * 检测心跳
     */
    public static void heartBeat() {
        int totalNum = 0;
        boolean kick = true;
        // 在线用户数
        int onlinePlayerCount = 0;
        int memoryPlayerCount = 0;
        for (GamePlayer player : playerMap.values()) {
            if (player != null) {
                // 每隔5秒检测一次
                long lostHeartBeatTime = System.currentTimeMillis() - player.getLastPingTime();
                if (lostHeartBeatTime > GameConfig.LOST_HEART_BEAT_TIME * DateHelper.SECOND_MILLIONS) {
                    // 超过一分钟 心跳不在 开始记录丢失次数
                    player.setLoseHeartTimes(player.getLoseHeartTimes() + 1);
                }
                // 6 次丢失, 时间大概为 90 s , 设置为离线状态
                boolean offLineFlag = false;
                if (player.getLoseHeartTimes() > GameConfig.LOST_HEART_BEAT_TIMES && player.getState() == PlayerState.ONLINE) {
                    // 只是设置用户下线
                    offLineFlag = true;
                    YanQuMessage castleReq = new YanQuMessage(Protocol.A_PLAYER_OFFLINE);
                    castleReq.setPlayerId(player.getUserId());
                    ServerLinkControl.send(player.getUserId(), castleReq);
                }

                boolean kickFlag = false;
                if (kick && lostHeartBeatTime > GameConfig.KICK_OUT_PLAYER_TIME * DateHelper.SECOND_MILLIONS) {
                    // 3分钟 分钟 将玩家移出内存, 每次定时任务只移除15个(5s)
                    if (!dbConnect) {
                        continue;
                    }
                    kickFlag = true;
                    YanQuMessage castleReq = new YanQuMessage(Protocol.A_KICK_OUT_PLAYER);
                    castleReq.setPlayerId(player.getUserId());
                    ServerLinkControl.send(player.getUserId(), castleReq);
                    totalNum++;
                    if (totalNum >= 3) {
                        kick = false;
                    }
                }
                // 记录下在线用户
                if (!offLineFlag && player.getState() == PlayerState.ONLINE) {
                    onlinePlayerCount++;
                }
                if (!kickFlag) {
                    memoryPlayerCount++;
                }
            }
        }
        logger.debug("内存用户数:{},在线用户数:{}", memoryPlayerCount, onlinePlayerCount);
    }

    public static void setDbConnect(boolean success) {
        dbConnect = success;
    }

    /**
     * 增加联姻分组重载监听
     */
    private void addMarriageGroupReloadListener() {
        CrossMarriageGroupMgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(PlayerModule.class).syncMarriageGroup();
            }
        });
    }

    public static CompletableFuture<SyncResponse> sendToCrossServer(long userId, int code, GeneratedMessage.Builder<?> requestBuilder, GeneratedMessage.Builder<?> responseBuilder) {
        long serverId = GameServer.getInstance().getServerId();
        ResultProcessor resultProcessor = syncMessageCaller.sendMsgSync(serverId, 0, userId, code, CrossProtocol.C_CROSS_SERVICE_HANDLER, 0, requestBuilder, responseBuilder);
        YanQuMessage request = resultProcessor.getRequest();
        sendPacket(0, request);
        return resultProcessor.getCompletableFuture();
    }

    public static CompletableFuture<SyncResponse> sendToActivityCrossServer(long userId, int code, int activityId, GeneratedMessage.Builder<?> requestBuilder, GeneratedMessage.Builder<?> responseBuilder) {
        long serverId = GameServer.getInstance().getServerId();
        ResultProcessor resultProcessor = syncMessageCaller.sendMsgSync(serverId, 0, userId, code, CrossProtocol.C_CROSS_SERVICE_HANDLER, activityId, requestBuilder, responseBuilder);
        YanQuMessage request = resultProcessor.getRequest();
        sendPacket(0, request);
        return resultProcessor.getCompletableFuture();
    }

    public static CompletableFuture<SyncResponse> sendToActivityCrossServerForUnionGroup(long userId, int code, int activityId, GeneratedMessage.Builder<?> requestBuilder, GeneratedMessage.Builder<?> responseBuilder) {
        long serverId = GameServer.getInstance().getServerId();
        ResultProcessor resultProcessor = syncMessageCaller.sendMsgSync(serverId, 0, userId, code, CrossProtocol.C_CROSS_SERVICE_HANDLER, activityId, requestBuilder, responseBuilder);
        YanQuMessage request = resultProcessor.getRequest();
        // 拿一下分组ID
        int groupId = GameUnionActivityGroupMgr.getViewDataGroupId(activityId, userId);
        GamePlayerMgr.sendUnionActivityGroupPacket(CrossProtocol.C_CROSS_SERVICE_HANDLER_ACTIVITY_GROUP, request.getMessage(), userId, activityId, groupId);
        return resultProcessor.getCompletableFuture();
    }

    public static CompletableFuture<SyncResponse> sendToGameServer(long forwardServerId, int code, long userId, GeneratedMessage.Builder<?> requestBuilder, GeneratedMessage.Builder<?> responseBuilder) {
        long serverId = GameServer.getInstance().getServerId();
        ResultProcessor resultProcessor = syncMessageCaller.sendMsgSync(serverId, forwardServerId, userId, code, CrossProtocol.C_SERVICE_FORWARD_MESSAGE, 0, requestBuilder, responseBuilder);
        YanQuMessage request = resultProcessor.getRequest();
        sendPacket(0, request);
        return resultProcessor.getCompletableFuture();
    }


    public static void initGameServiceCommand() {
        syncMessageService.addGameServiceCommand(GameProtocol.S_SERVICE_WINERY_VISIT, new WineryVisitICrossServiceCmd());
        syncMessageService.addGameServiceCommand(GameProtocol.S_USER_INFO_DETAIL_SERVICE, new UserInfoDetailServiceCmd());
        syncMessageService.addGameServiceCommand(GameProtocol.S_CRICKET_DEFENDER_HANDLE_BATTLE_LOG, new CricketDefendHandleBattleLogServiceCmd());
        syncMessageService.addGameServiceCommand(GameProtocol.S_CRICKET_DEFENDER_HANDLE_SUCCESS, new CricketDefendHandleSuccessServiceCmd());
        syncMessageService.addGameServiceCommand(Protocol.S_GODDESS_SERVICE_WATCH_OTHER_USER, new GoddessWatchUserServiceCmd());
        syncMessageService.addGameServiceCommand(Protocol.S_CURIO_SERVICE_WATCH_OTHER_USER, new CurioWatchUserServiceCmd());
        syncMessageService.addGameServiceCommand(Protocol.S_CURIO_SERVICE_WATCH_OTHER_ITEM, new CurioWatchItemServiceCmd());
        syncMessageService.addGameServiceCommand(Protocol.S_QIXI_GET_RELATION_TOP_GET_LIST_SERVICE, new QixiRelationTopGetListServiceCmd());
        syncMessageService.addGameServiceCommand(Protocol.S_SUMMER_GAMES_SEARCH_USER_SERVICE, new SummerGamesSearchUserServiceCommand());
        TombMgr.initService(syncMessageService);
        CricketMgr.initService(syncMessageService);
        SilkRoadMgr.initService(syncMessageService);
        EveFeastMgr.initService(syncMessageService);

        UnionCampWarPatronsMgr.initService(syncMessageService);

        QixiMgr.initService(syncMessageService);
        GuaGiftsMgr.initService(syncMessageService);

        SnowGardenMgr.initService(syncMessageService);
        ActivityMallDayBuyMgr.initService(syncMessageService);
        ZhenHuanMgr.initService(syncMessageService);
    }

    public static GameService getSyncMessageProvider() {
        return syncMessageService;
    }


    public static Caller getSyncMessageCaller() {
        return syncMessageCaller;
    }

    public static GameService getSyncMessageService() {
        return syncMessageService;
    }

    /**
     * 离开系统标识
     */
    public static void removeSystem(GamePlayer player){
        try {
            DunHuangShiKuMgr.removeActivity(player);
            MonopolyMarketMgr.offLine(player);
            NewMonopolyMarketMgr.offLine(player);
            CrossBanquetMgr.removeActivity(player);
            ShuiHuYeZiCardMgr.offLine(player);
            SeacraftSouthSeaMgr.offLine(player);
            ShuShanMgr.exitSystem(player);
            XiuXianMgr.offLine(player);
            ShanHeFeastMgr.offLine(player);
            PeakSeacraftMgr.offLine(player);
            RecallPlayerMgr.logOutClearData(player);
            player.getModule(SignInDrawModule.class).offLine();
            KuangMgr.offLine(player);
            JiuZhouMgr.offLine(player);
            // 海神岛
            player.getModule(HsIslandActivityModule.class).offLine();
        }catch (Exception e){
            logger.error("removeSystem error",e);
        }

        try {
            CricketMgr.offLine(player);
        } catch (Exception e) {
            logger.error("removeSystem CricketMgr error",e);
        }
        try {
            player.getModule(DailyDataModule.class).calcLiveShowTime(false, true);
        }catch (Exception e){
            logger.error("calcLiveShowTime error:", e);
        }
    }

    private void addMallRebateReloadListener() {
        MallRebateMgr.addReloadListener(() -> {
            List<GamePlayer> playerList = getCopyPlayerList();
            for (GamePlayer player : playerList) {
                player.getModule(MallModule.class).syncMallRebate();
            }
        });
    }

    /**
     * 区服 发送到指定活动跨服
     *
     * @param code
     * @param messageBuilder
     * @param userId
     * @param appointCrossId
     */
    public static void sendAppointActivityPacket(int code, AbstractMessage.Builder<?> messageBuilder, long userId, int appointCrossId) {
        boolean isActivityCrossCode = code > 40000 && code < 50000;
        if (!isActivityCrossCode) {
            throw new RuntimeException("============================= 这个不是活动跨服协议ID，用这个发要死循环了 =============================");
        }
        ActivityProto.AgentTransferToAppointCrossMsg.Builder newBuilder = ActivityProto.AgentTransferToAppointCrossMsg.newBuilder()
                .setCode(code)
                .setMsg(messageBuilder.build().toByteString())
                .setCrossId(appointCrossId);
        YanQuMessage message = YanQuMessageUtils.buildMessage(AgentProtocol.A_TRANSFER_TO_APPOINT_ACTIVITY, userId, newBuilder);
        ServerLinkControl.send(userId, message);
    }

    /**
     * 区服 发送到指定活动跨服
     *
     * @param code
     * @param messageBuilder
     * @param userId
     * @param appointCrossId
     * @param groupId
     */
    public static void sendAppointActivityPacket(int code, AbstractMessage.Builder<?> messageBuilder, long userId, int appointCrossId, int groupId) {
        boolean isActivityCrossCode = code > 40000 && code < 50000;
        if (!isActivityCrossCode) {
            throw new RuntimeException("============================= 这个不是活动跨服协议ID，用这个发要死循环了 =============================");
        }
        ActivityProto.AgentTransferToAppointCrossMsg.Builder newBuilder = ActivityProto.AgentTransferToAppointCrossMsg.newBuilder()
                .setCode(code)
                .setMsg(messageBuilder.build().toByteString())
                .setCrossId(appointCrossId)
                .setGroupId(groupId);
        YanQuMessage message = YanQuMessageUtils.buildMessage(AgentProtocol.A_TRANSFER_TO_APPOINT_ACTIVITY, userId, newBuilder);
        ServerLinkControl.send(userId, message);
    }

    public static long getGamePlayerCount(){
        return playerMap.size();
    }

    /**
     * 发邮件踢下线
     */
    public static void kickAndSendMailBySecondCmdMonitor(GamePlayer player, int code) {
        String mailTitle = MultipleLanguageMgr.getContent("VIOLATION_BEHAVIOR_PUNISH_TITLE", player.getLanguage());
        String mailContent = MultipleLanguageMgr.getContent("VIOLATION_BEHAVIOR_PUNISH_CONTENT", player.getLanguage());
        MailManager.sendMail(player.getUserId(), eMailType.SYSTEM.getValue(), "", mailContent, mailTitle);

        CommandStatisticsMgr.addSecondCommandKickData(player.getUserId(), code);

        //踢下线
        AgentPlayer.executeKillPlayer(player.getUserId());
    }
}
